package com.autumn.config.services.provider;

import com.alibaba.dubbo.config.annotation.Service;
import com.autumn.config.api.CloudConfigService;
import com.autumn.config.api.consts.ConfigureConsts.ConnectionTypeConsts;
import com.autumn.config.api.consts.ConfigureConsts.StatusConsts;
import com.autumn.config.api.consts.ConnectionTypeInfo;
import com.autumn.config.api.services.ICacheService;
import com.autumn.config.api.vo.*;
import com.autumn.config.entities.ApplicationInfo;
import com.autumn.config.entities.ApplicationProfile;
import com.autumn.config.entities.ConnectionConfigure;
import com.autumn.config.entities.view.ServiceConnectionConfigureView;
import com.autumn.config.entities.view.ServiceDataSourceConfigureView;
import com.autumn.config.repositories.IApplicationInfoRepository;
import com.autumn.config.repositories.IApplicationProfileRepository;
import com.autumn.config.repositories.IConnectionConfigureRepository;
import com.autumn.config.repositories.view.IServiceConnectionConfigureViewRepository;
import com.autumn.config.repositories.view.IServiceDataSourceConfigureViewRepository;
import com.autumn.mybatis.criterion.Query;
import com.autumn.spring.boot.properties.AutumnWebProperties;
import com.autumn.spring.boot.properties.PropertiesConstant;
import com.autumn.util.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.stereotype.Component;

import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 
 * @author 老码农
 *
 *         2018-06-28 16:49:01
 */
@Service(interfaceClass = CloudConfigService.class)
@Component
public class CloudConfigServiceImpl implements CloudConfigService {

	@Autowired
	private ICacheService cacheService;

	@Autowired
	private IServiceConnectionConfigureViewRepository cfgRepository;

	@Autowired
	private IServiceDataSourceConfigureViewRepository dataRepository;

	@Autowired
	private IApplicationInfoRepository repository;

	@Autowired
	private IApplicationProfileRepository profileRepository;

	@Autowired
	private IConnectionConfigureRepository connRepository;

	@Autowired
	private org.springframework.core.env.ConfigurableEnvironment env;

	@Override
	public List<ServiceConnectionConfigureValue> serviceConnectionList(ServiceBaseValue service) {
		ExceptionUtils.checkNotNull(service, "service");
		if (service.getServiceId() == null) {
			ExceptionUtils.throwValidationException("服务 id 不能为空。");
		}
		return cacheService.getServiceConnectionList(service.getServiceId(), (serviceId) -> {
			Query query = new Query(ServiceConnectionConfigureView.class);
			query.eq("serviceId", serviceId).eq("isDelete", false).eq("appStatus", StatusConsts.STATUS_RELEASE)
					.eq("connectionStatus", StatusConsts.STATUS_RELEASE).orderBy("sortId").orderBy("id");
			List<ServiceConnectionConfigureView> connList = cfgRepository.selectList(query.builderSection());
			return AutoMapUtils.mapList(connList, ServiceConnectionConfigureValue.class);
		});
	}

	@Override
	public ServiceConnectionConfigureValue serviceConnection(ServiceIdentityValue identity) {
		ExceptionUtils.checkNotNull(identity, "identity");
		if (identity.getServiceId() == null) {
			ExceptionUtils.throwValidationException("服务 id 不能为空。");
		}
		if (identity.getIdentity() == null) {
			ExceptionUtils.throwValidationException("连接标识 id 不能为空。");
		}
		ServiceConnectionConfigureValue result = cacheService.getServiceConnection(identity.getServiceId(),
				identity.getIdentity(), (s, i) -> {
					Query query = new Query(ServiceConnectionConfigureView.class);
					query.eq("serviceId", s).eq("isDelete", false).eq("identity", i)
							.eq("appStatus", StatusConsts.STATUS_RELEASE)
							.eq("connectionStatus", StatusConsts.STATUS_RELEASE);
					ServiceConnectionConfigureView cfgView = cfgRepository.selectFirst(query.builderSection());
					return AutoMapUtils.map(cfgView, ServiceConnectionConfigureValue.class);
				});
		if (result != null && identity.getConnectionType() != null) {
			if (!result.getConnectionType().equals(identity.getConnectionType())) {
				return null;
			}
		}
		return result;
	}

	@Override
	public ServiceDataSourceConfigureValue serviceTargetDataSource(ServiceDataSourceValue target) {
		ExceptionUtils.checkNotNull(target, "target");
		if (StringUtils.isNullOrBlank(target.getServiceId())) {
			ExceptionUtils.throwValidationException("服务 id 不能为空。");
		}
		if (StringUtils.isNullOrBlank(target.getTargetServiceId())) {
			ExceptionUtils.throwValidationException("目标服务 id 不能为空。");
		}
		if (target.getConnectionType() == null) {
			ExceptionUtils.throwValidationException("连接类型不能为空。");
		}
		return cacheService.getServiceTargetDataSource(target, (in) -> {
			Query query = new Query(ServiceDataSourceConfigureView.class);
			query.eq("serviceId", in.getServiceId()).eq("targetServiceId", in.getTargetServiceId())
					.eq("connectionType", in.getConnectionType()).orderBy("id");
			ServiceDataSourceConfigureView dataSource = dataRepository.selectFirst(query.builderSection());
			if (dataSource == null) {
				return null;
			}
			if (dataSource.getAppIsDelete() || dataSource.getConnectionIsDelete()
					|| dataSource.getTargetAppIsDelete()) {
				return null;
			}
			if (StatusConsts.STATUS_RELEASE != dataSource.getAppStatus()
					|| StatusConsts.STATUS_RELEASE != dataSource.getTargetAppStatus()
					|| StatusConsts.STATUS_RELEASE != dataSource.getConnectionStatus()) {
				return null;
			}
			return AutoMapUtils.map(dataSource, ServiceDataSourceConfigureValue.class);
		});
	}

	@Override
	public ApplicationInfoValue serviceQueryById(ServiceBaseValue service) {
		ExceptionUtils.checkNotNull(service, "service");
		if (StringUtils.isNullOrBlank(service.getServiceId())) {
			ExceptionUtils.throwValidationException("服务 id 不能为空。");
		}
		return cacheService.getServiceQueryById(service.getServiceId(), (serviceId) -> {
			Query query = new Query(ApplicationInfo.class);
			query.eq("appId", serviceId).eq("isDelete", false).eq("status", StatusConsts.STATUS_RELEASE);
			return AutoMapUtils.map(this.repository.selectFirst(query.builderSection()), ApplicationInfoValue.class);
		});
	}

	@Override
	public ServiceProfileInfo serviceProfile(ServiceProfileValue profile) {
		ExceptionUtils.checkNotNull(profile, "profile");
		if (StringUtils.isNullOrBlank(profile.getServiceId())) {
			ExceptionUtils.throwValidationException("服务 id 不能为空。");
		}
		if (StringUtils.isNullOrBlank(profile.getProfile())) {
			ExceptionUtils.throwValidationException("配置 不能为空。");
		}

		return cacheService.getServiceProfile(profile, (i) -> {
			ApplicationInfoValue app = this.serviceQueryById(profile);
			if (app == null) {
				return null;
			}

			Query query = new Query(ApplicationProfile.class);
			query.eq("appId", app.getId()).eq("name", profile.getProfile()).eq("status", StatusConsts.STATUS_RELEASE);
			if (!StringUtils.isNullOrBlank(profile.getLabel())) {
				query.eq("label", profile.getLabel());
			}
			query.orderByDescending("isDefault").orderBy("id");
			ApplicationProfile appProfile = profileRepository.selectFirst(query.builderSection());
			if (appProfile == null) {
				return null;
			}
			Map<String, Object> profileMap;
			if (ApplicationProfile.FORAMT_YML.equalsIgnoreCase((appProfile.getProfileFormat()))) {
				profileMap = AutumnContextUtils.parseYml(appProfile.getContent(), false);
			} else {
				profileMap = AutumnContextUtils.parseProperties(appProfile.getContent(), false);
			}
			setRef(profileMap);
			profileMap.remove("info.service.id");
			profileMap.remove("info.service.name");
			profileMap.remove("info.service.port");
			profileMap.put("info.service.id", app.getAppId());
			profileMap.put("info.service.name", app.getAppName());
			profileMap.put("info.service.port", "${server.port}");

			String profileName = null;
			if (!StringUtils.isNullOrBlank(appProfile.getProfileFormat())) {
				profileName = String.format("%s-%s.%s", profile.getServiceId(), profile.getProfile(), appProfile.getProfileFormat());
			} else {
				profileName = String.format("%s-%s.%s", profile.getServiceId(), profile.getProfile(), "yml");
			}

			ServiceProfileInfo info = new ServiceProfileInfo();
			info.setAppId(TypeUtils.toConvert(Long.class, app.getId()));
			info.setAppName(app.getAppName());
			info.setId(appProfile.getId());
			info.setLabel(appProfile.getLabel());
			info.setName(appProfile.getName());
			info.setVersion(appProfile.getVersion());
			info.setProfileFormat(appProfile.getProfileFormat());
			info.setProfileName(profileName);

			addDefaultProperties(profileMap);

			if (profileMap instanceof LinkedHashMap) {
				info.setProfileMap((LinkedHashMap<String, Object>) profileMap);
			} else {
				info.setProfileMap(new LinkedHashMap<>(profileMap));
			}
			return info;

		});
	}

	private Map<String, Map<String, Object>> DEFAULT_PROPERTIES_MAP = new ConcurrentHashMap<>();

	private Map<String, Object> getDefaultProperties(final String name, final String configPrefix) {
		return DEFAULT_PROPERTIES_MAP.computeIfAbsent(name, key -> {
			Map<String, Object> resolverMap = new RelaxedPropertyResolver(this.env, key).getSubProperties(".");
			if (resolverMap == null) {
				resolverMap = new LinkedHashMap<>();
			}
			Map<String, Object> map = AutumnContextUtils.showProperties(resolverMap);
			String prefix;
			if (!configPrefix.endsWith(".")) {
				prefix = configPrefix + ".";
			} else {
				prefix = configPrefix;
			}
			AutumnContextUtils.handlePropertiesName(map, prefix);
			return map;
		});
	}

	private void addDefaultProperties(Map<String, Object> profileMap) {
		Map<String, Object> defaultMap = this.getDefaultProperties(AutumnWebProperties.PREFIX,
				AutumnWebProperties.PREFIX);
		if (defaultMap != null) {
			Object publistType = defaultMap.get(AutumnWebProperties.PUBLISH_TYPE_PROPERTIES);
			if (publistType != null) {
				profileMap.put(AutumnWebProperties.PUBLISH_TYPE_PROPERTIES, publistType);
			}
		}

		defaultMap = this.getDefaultProperties(PropertiesConstant.AUTUMN_MESSAGE_PROPERTIES_PREFIX,
				PropertiesConstant.AUTUMN_MESSAGE_PROPERTIES_PREFIX);
		profileMap.putAll(defaultMap);

		defaultMap = this.getDefaultProperties(PropertiesConstant.AUTUMN_CLOUD_PROPERTIES_PREFIX,
				PropertiesConstant.AUTUMN_CLOUD_PROPERTIES_PREFIX);
		profileMap.putAll(defaultMap);
	}

	/**
	 * 设置引用
	 * 
	 * @param propertiesMap
	 *            属性 Map
	 */
	private void setRef(Map<String, Object> propertiesMap) {
		Map<String, List<String>> refMap = ApplicationProfile.readRefSet(propertiesMap);
		for (Map.Entry<String, List<String>> entry : refMap.entrySet()) {
			Query query = new Query(ConnectionConfigure.class);
			query.eq("name", entry.getKey()).eq("status", StatusConsts.STATUS_RELEASE).eq("isDelete", false)
					.orderBy("id");
			ConnectionConfigure configure = connRepository.selectFirst(query.builderSection());
			Map<String, Object> propertyMap = null;
			if (configure != null) {
				propertyMap = AutumnContextUtils.parseProperties(configure.getPropertyContent(), false);
			}
			for (String propertiesName : entry.getValue()) {
				propertiesMap.remove(propertiesName);
				if (propertyMap != null) {
					for (Map.Entry<String, Object> propertiesEntry : propertyMap.entrySet()) {
						propertiesMap.put(propertiesName + "." + propertiesEntry.getKey(), propertiesEntry.getValue());
					}
				}
			}
		}
	}

	@Override
	public Collection<ConnectionTypeInfo> connectionTypeList() {
		return ConnectionTypeConsts.getConnectionTypeInfos();
	}

}
